import aiofiles
import os
import time
from datetime import datetime, timedelta, timezone
from typing import Annotated, Literal, Union
from uuid import UUID

import jwt
import uvicorn
from fastapi import FastAPI, Path, Query, Body, Cookie, Header, status, Form, File, UploadFile, HTTPException, Request, \
    Depends
from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi.openapi.docs import get_swagger_ui_html, get_swagger_ui_oauth2_redirect_html, get_redoc_html
from fastapi.responses import HTMLResponse,JSONResponse,PlainTextResponse
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt import InvalidTokenError
from passlib.context import CryptContext
from starlette.staticfiles import StaticFiles
from pydantic import BaseModel, Field, HttpUrl, EmailStr





# 创建FastAPI实例，禁用掉FastAPI会自动生成Swagger UI和Redoc文档
app = FastAPI(docs_url=None, redoc_url=None)

# 静态文件挂载（路由与静态文件映射）
app.mount("/static", StaticFiles(directory="../statics"), name="static")


# 重新提供一个Swagger UI，相当于我们自己重写了这么一个路由
@app.get("/docs", include_in_schema=False)
def custom_swagger_ui():
    return get_swagger_ui_html(
        openapi_url = "/openapi.json",
        title = "Swagger UI for FastAPI",
        swagger_js_url="/static/swagger-ui-bundle.min.js",
        swagger_css_url="/static/swagger-ui.min.css"
    )

# 这里与Swagger UI一样，重新提供Redoc
@app.get("/redoc", include_in_schema=False)
def custom_redoc():
    return get_redoc_html(
        openapi_url = "/openapi.json",
        title = "Redoc for FastAPI",
        redoc_js_url="/static/redoc.standalone.js"
    )

fake_user_db = {
    "johndoe": {
        "username": "johndoe",
        "full_name": "John Doe",
        "email": "johndoe@example.com",
        "hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
        "disabled": False,
    },
    "alice": {
        "username": "alice",
        "full_name": "Alice Smith",
        "email": "alice@example.com",
        "hashed_password": "fakehashedsecret",
        "disabled": True,
    }
}

SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

# 创建用于密码哈希和身份校验的PassLib上下文
pwd_content = CryptContext(schemes=["bcrypt"], deprecated="auto")

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")


def fake_hash_password(password:str):
    return "fakehashed" + password

def verify_password(plain_password, hashed_password):
    return pwd_content.verify(plain_password, hashed_password)

def get_password_hash(password):
    return pwd_content.hash(password)



class Token(BaseModel):
    access_token: str
    token_type: str

class User(BaseModel):
    username: str
    email: str | None = None
    full_name: str | None = None
    disabled: bool | None = None

class UserInDB(User):
    hashed_password: str

def get_user(db, username: str):
    if username in db:
        user_dict = db[username]
        return UserInDB(**user_dict)

def authenticate_user(fake_db, username: str, password: str):
    user = get_user(fake_db, username)
    if not user:
        return False
    if not verify_password(password, user.hashed_password):
        return False
    return user

def create_access_token(data: dict, expires_delta: timedelta | None = None):
    to_encode = data.copy()
    if expires_delta:
        expire = datetime.now(timezone.utc) + expires_delta
    else:
        expire = datetime.now(timezone.utc) + timedelta(minutes=15)
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

def fake_decode_token(token):
    user = get_user(fake_user_db, token)
    return user
async def get_current_user(token: str = Depends(oauth2_scheme)):
    # user = fake_decode_token(token)
    # if not user:
    #     raise HTTPException(
    #         status_code=status.HTTP_401_UNAUTHORIZED,
    #         detail="Invalid authentication credentials",
    #         headers={"WWW-Authenticate": "Bearer"},
    #     )
    # return user
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Could not validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        # 在JWT中，sub（Subject）是标准声明之一，用于标识该JWT的主体，即用户。
        # 当然也可以使用username作为键，但JWT标准推荐使用sub
        username = payload.get("sub")
        if username is None:
            raise credentials_exception
        # token_data = TokenData(username=username)
    except InvalidTokenError:
        raise credentials_exception
    user = get_user(fake_user_db, username)
    if user is None:
        raise credentials_exception
    return user

async def get_current_active_user(current_user: User = Depends(get_current_user)):
    if current_user.disabled:
        raise HTTPException(status_code=400, detail="Inactive user")
    return current_user

@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()) -> Token:
    # user_dict = fake_user_db.get(form_data.username)
    # if not user_dict:
    #     raise HTTPException(status_code=400, details="Incorrect username or password")
    # user = UserInDB(**user_dict)
    # hased_password = fake_hash_password(form_data.password)
    # if not hased_password == user.hashed_password:
    #     raise HTTPException(status_code=400, details="Incorrect username or password")
    #
    # return {"access_token": user.username, "token_type": "bearer"}
    user = authenticate_user(fake_user_db, form_data.username, form_data.password)
    if not user:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Incorrect username or password",
            headers={"WWW-Authenticate": "Bearer"},
        )
    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user.username}, expires_delta=access_token_expires
    )
    return Token(access_token=access_token, token_type="bearer")


@app.get("/users/me")
async def read_users_me(current_user: User = Depends(get_current_active_user)):
    return current_user


@app.get("/items")
async def get_items(token: str = Depends(oauth2_scheme)):
    return {"token": token}

if __name__ == '__main__':
    app.debug = True
    uvicorn.run("main:app", host="127.0.0.1", port=8026, reload=True)

